package com.example.demo.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Liunh
 */
@Slf4j
public class FileUtil {

    /**
     * 1M
     */
    private int readFileSize = 1024 * 1024;

    private FileUtil() {
    }

    private static volatile FileUtil instance = null;

    public static FileUtil getInstance() {
        if (null == instance) {
            synchronized (FileUtil.class) {
                if (null == instance) {
                    instance = new FileUtil();
                }
            }
        }
        return instance;
    }

    public String fileSavePath() {
        String folderPath = "C:/Users/Public/Documents/";
        if (EnvUtil.isLinux()) {
            folderPath = "/usr/local/app/files/exportFiles/";
            if (!this.folderExists(folderPath)) {
                this.folderCreate(folderPath);
            }
        }
        return folderPath;
    }

    public boolean folderExists(String folderPath) {
        try {
            File file = new File(folderPath);
            if (file.exists() && file.isDirectory()) {
                return true;
            }
            log.info(">>>>>> 文件夹不存在,文件夹路径：{}", folderPath);
        } catch (Exception e) {
            log.error(">>>>>> 读取文件夹路径异常", e);
        }
        return false;
    }


    public boolean fileExists(String filePath) {
        try {
            File file = new File(filePath);
            if (file.exists() && file.isFile()) {
                return true;
            }
            log.info(">>>>>> 文件不存在,文件路径：{}", filePath);
        } catch (Exception e) {
            log.error(">>>>>> 读取文件异常", e);
        }
        return false;
    }

    public boolean folderCreate(String folderPath) {
        try {
            File file = new File(folderPath);
            if (file.exists() && file.isDirectory()) {
                return true;
            }
            log.info(">>>>>> 文件夹不存在,创建文件夹,路径：{}", folderPath);
            return file.mkdirs();
        } catch (Exception e) {
            log.error(">>>>>> 读取文件夹路径异常", e);
        }
        return false;
    }

    public boolean fileCreate(String filePath) {
        try {
            File file = new File(filePath);
            if (file.exists() && file.isFile()) {
                return true;
            }
            log.info(">>>>>> 文件不存在,创建文件,路径：{}", filePath);
            return file.createNewFile();
        } catch (IOException e) {
            log.error(">>>>>> 读取文件路径异常", e);
        }
        return false;
    }

    /**
     * 获取文件修改时间
     *
     * @param fileName
     * @return
     */
    public Date getFileModifiedTime(String fileName) {
        File file = new File(fileName);
        Calendar cal = Calendar.getInstance();
        long time = file.lastModified();
        cal.setTimeInMillis(time);
        log.info("FileUtils --> getFileModifiedTime: {}", JacksonUtil.beanToJson(cal.getTime()));
        return cal.getTime();
    }

    /**
     * @param filePathName
     * @return
     */
    public String readFile(String filePathName) {
        FileInputStream fis = null;
        FileChannel channel = null;
        StringBuilder content = new StringBuilder();
        try {
            ResourceLoader resolver = new PathMatchingResourcePatternResolver();
            Resource resource1 = resolver.getResource(filePathName);
            byte[] bytes = new byte[readFileSize];
            ByteBuffer byteBuffer = ByteBuffer.allocate(readFileSize);
            fis = new FileInputStream(resource1.getFile());
            channel = fis.getChannel();
            int len;
            while ((len = channel.read(byteBuffer)) != -1) {
                byteBuffer.flip();
                byteBuffer.get(bytes, 0, len);
                content.append(new String(bytes, 0, readFileSize));
                byteBuffer.clear();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            try {
                if (null != channel) {
                    channel.close();
                }
                if (null != fis) {
                    fis.close();
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        return content.toString();
    }

    /**
     * 读取文件内容
     * 读服务器端大文件
     *
     * @param filePathName 来源路径
     */
    public List<String> readBigFile(String filePathName) {
        log.info("读出文件内容操作,文件路径：{}", filePathName);
        long dateTimeStart = System.currentTimeMillis();

        List<String> resultList = new ArrayList<>();
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        InputStreamReader isr = null;
        BufferedReader in = null;
        try {
            File file = new File(filePathName);
            if (!file.exists()) {
                log.info("读出文件不存在,文件路径：{}", filePathName);
                return resultList;
            }
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);
            isr = new InputStreamReader(bis, StandardCharsets.UTF_8);
            //10M
            in = new BufferedReader(isr, 10 * readFileSize);
            while (in.ready()) {
                resultList.add(in.readLine());
            }
        } catch (IOException e) {
            log.error("读取文件异常", e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (fis != null) {
                    fis.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (isr != null) {
                    isr.close();
                }
            } catch (IOException e) {
                log.error("读取文件关闭文件流异常", e);
            }
        }
        log.info("读出文件内容耗时:{}", System.currentTimeMillis() - dateTimeStart);
        return resultList;
    }


    /**
     * 方法追加文件内容：使用RandomAccessFile
     *
     * @param fileName
     * @param content
     */
    public void appendFile(String fileName, String content) {
        RandomAccessFile randomFile = null;
        try {
            randomFile = new RandomAccessFile(fileName, "rw");
            long fileLength = randomFile.length();
            randomFile.seek(fileLength);
            randomFile.writeBytes(content);
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                if (null != randomFile) {
                    randomFile.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
    }

    /**
     * 上传文件
     * <p>
     * 用于读面向行的格式化大文件
     *
     * @param inputFile  来源路径
     * @param outputFile 上传路径
     */
    public String upload(String inputFile, String outputFile) {
        log.info("上传文件,inputFile:{},outputFile:{}", inputFile, outputFile);
        long dateTimeStart = System.currentTimeMillis();
        boolean result = Boolean.FALSE;

        FileInputStream fis = null;
        BufferedInputStream bis = null;
        InputStreamReader isr = null;
        BufferedReader in = null;
        FileWriter fw = null;
        try {
            File srcfile = new File(inputFile);
            File file = new File(outputFile);
            //如果文件夹不存在则创建
            if (!file.exists()) {
                log.info("文件保存路径不存在,创建保存路径：{}", outputFile);
                if (file.mkdirs()) {
                    log.info("文件保存路径生成成功:{}", outputFile);
                }
            }
            fis = new FileInputStream(srcfile);
            bis = new BufferedInputStream(fis);
            isr = new InputStreamReader(bis, StandardCharsets.UTF_8);
            //10M
            in = new BufferedReader(isr, 10 * readFileSize);
            outputFile = outputFile + File.separatorChar + srcfile.getName();
            fw = new FileWriter(outputFile);
            String contentLine = null;
            while (in.ready()) {
                contentLine = in.readLine();
                fw.append(contentLine).append("\n");
            }
            fw.flush();
            result = Boolean.TRUE;
        } catch (IOException e) {
            log.error("上传文件异常", e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (in != null) {
                    in.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (Exception e) {
                log.error("上传文件异常", e);
            }
        }
        log.info("上传文件:{}", System.currentTimeMillis() - dateTimeStart);
        return result ? outputFile : null;
    }

    /**
     * 上传文件
     * 用于读面向行的格式化大文件
     * @param inputFile  来源路径
     * @param outputFile 上传路径
     */
    public String copy(String inputFile, String outputFile) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(inputFile);
            fos = new FileOutputStream(outputFile);
            IOUtils.copy(fis, fos);
        } catch (IOException e) {
            log.error("上传文件异常", e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (Exception e) {
                log.error("上传文件异常", e);
            }
        }
        return outputFile;
    }

    /**
     * 删除单个文件
     *
     * @param fileName 要删除的文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    public boolean deleteFile(String fileName) {
        boolean result = Boolean.FALSE;
        try {
            File file = new File(fileName);
            // 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
            if (file.exists() && file.isFile()) {
                if (file.delete()) {
                    log.info("删除文件" + fileName + " 成功！");
                    result = Boolean.TRUE;
                } else {
                    log.error("删除文件" + fileName + " 失败！");
                }
            } else {
                log.error("删除文件失败：" + fileName + " 不存在！");
            }
        } catch (Exception e) {
            log.error("删除文件" + fileName + " 异常！");
        }
        return result;
    }

    /**
     * 判断字符串中是否包含中文
     *
     * @param str 待校验字符串
     * @return 是否为中文
     * @warn 不能校验是否为中文标点符号
     */
    public boolean isContainChinese(String str) {
        Pattern pattern = Pattern.compile("[\u4e00-\u9fa5]");
        Matcher m = pattern.matcher(str);
        if (m.find()) {
            return true;
        }
        return false;
    }

    /**
     * 判断文件大小
     *
     * @param :multipartFile:上传的文件
     * @param sizeLimit:           限制大小
     * @param unit:限制单位（B,K,M,G)
     * @return boolean:是否大于
     */
    public boolean fileIsTooBig(MultipartFile multipartFile, int sizeLimit, String unit) {
        //准备接收换算后文件大小的容器
        double fileSize = checkFileSize(multipartFile, unit);
        //如果上传文件大于限定的容量
        if (fileSize > sizeLimit) {
            return true;
        }
        return false;
    }

    /**
     * 文件大小
     *
     * @param :multipartFile:上传的文件
     * @param unit:限制单位（B,K,M,G)
     * @return 文件大小
     */
    public double checkFileSize(MultipartFile multipartFile, String unit) {
        //上传文件的大小, 单位为字节.
        long len = multipartFile.getSize();
        //准备接收换算后文件大小的容器
        double fileSize = 0.0d;
        if (FileUnit.B.getUnit().equals(unit.toUpperCase())) {
            fileSize = (double) len;
        } else if (FileUnit.K.name().equals(unit.toUpperCase()) || FileUnit.K.getUnit().equals(unit.toUpperCase())) {
            fileSize = (double) len / 1024;
        } else if (FileUnit.M.name().equals(unit.toUpperCase()) || FileUnit.M.getUnit().equals(unit.toUpperCase())) {
            fileSize = (double) len / 1048576;
        } else if (FileUnit.G.name().equals(unit.toUpperCase()) || FileUnit.G.getUnit().equals(unit.toUpperCase())) {
            fileSize = (double) len / 1073741824;
        }
        return fileSize;
    }

    /**
     * 获取文件的 bsolute path
     *
     * @return
     */
    public String getFilePath(String location) {
        try {
            ResourceLoader resolver = new PathMatchingResourcePatternResolver();
            Resource resource = resolver.getResource(location);
            return resource.getFile().getAbsolutePath();
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return "";
    }

    /**
     * 获取项目的 class path
     *
     * @return
     */
    public String getClassPath() {
        try {
            ResourceLoader resolver = new PathMatchingResourcePatternResolver();
            Resource resource = resolver.getResource("classpath:");
            String getAbsolutePath = resource.getFile().getAbsolutePath();
            log.info(getAbsolutePath);
            return getAbsolutePath;
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return "";
    }

    enum FileUnit {

        B("B"),
        K("KB"),
        M("MB"),
        G("GB");

        private String unit;

        FileUnit(String unit) {
            this.unit = unit;
        }

        public String getUnit() {
            return this.unit;
        }
    }


    /*public static void main(String[] args) {
        FileUtil fileUtil = FileUtil.getInstance();
        System.out.print(fileUtil.getClassPath());
    }*/

}
